/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.externaltools.internal.ui; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.externaltools.internal.IExternalToolConstants; import org.eclipse.core.externaltools.internal.model.BuilderCoreUtils; import org.eclipse.core.externaltools.internal.model.ExternalToolBuilder; import org.eclipse.core.resources.ICommand; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceDescription; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationListener; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.ElementListSelectionDialog; import org.eclipse.ui.dialogs.PropertyPage; import org.eclipse.ui.externaltools.internal.launchConfigurations.ExternalToolsMainTab; import org.eclipse.ui.externaltools.internal.launchConfigurations.ExternalToolsUtil; import org.eclipse.ui.externaltools.internal.launchConfigurations.IgnoreWhiteSpaceComparator; import org.eclipse.ui.externaltools.internal.model.BuilderUtils; import org.eclipse.ui.externaltools.internal.model.ExternalToolsPlugin; import org.eclipse.ui.externaltools.internal.model.IExternalToolsHelpContextIds; import org.eclipse.ui.externaltools.internal.model.IPreferenceConstants; import org.eclipse.ui.progress.IProgressService; /** * Property page to add external tools builders. */ public final class BuilderPropertyPage extends PropertyPage implements ICheckStateListener { //locally mark a command's enabled state so it can be processed correctly on performOK private static final String COMMAND_ENABLED= "CommandEnabled"; //$NON-NLS-1$ private Button upButton, downButton, newButton, importButton, editButton, removeButton; private boolean userHasMadeChanges= false; private List<ILaunchConfiguration> configsToBeDeleted = null; private List<ICommand> commandsToBeDeleted = null; private CheckboxTableViewer viewer= null; private boolean fWarned = false; /** * Flag to know if we can perform an edit of the selected element(s) */ private boolean fCanEdit = false; private ILabelProvider labelProvider= new BuilderLabelProvider(); /** * Error configs are objects representing entries pointing to * invalid launch configurations */ public class ErrorConfig { private ICommand command; public ErrorConfig(ICommand command) { this.command= command; } public ICommand getCommand() { return command; } } /** * Collection of configurations created while the page is open. * Stored here so they can be deleted if the page is cancelled. */ private List<ILaunchConfiguration> newConfigList = new ArrayList<ILaunchConfiguration>(); private SelectionListener buttonListener= new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { handleButtonPressed((Button) e.widget); } }; /** * Launch configuration listener which is responsible for updating items in * the tree when the user renames configurations in the dialog. * * This is necessary because when we tell the configuration dialog to open * on a launch config and the user renames that config, the old config (the * one in the tree) is made obsolete and a new config is created. This * listener hears when new configurations are created this way and replaces * the old configuration with the new. */ private ILaunchConfigurationListener configurationListener= new ILaunchConfigurationListener() { /** * A launch configuration has been added. If this config has been * movedFrom a configuration in the tree, replace the old config with * the new. */ @Override public void launchConfigurationAdded(final ILaunchConfiguration configuration) { ILaunchManager manager= DebugPlugin.getDefault().getLaunchManager(); final ILaunchConfiguration oldConfig= manager.getMovedFrom(configuration); if (oldConfig == null) { return; } //Replace the movedFrom config in the list of newly created configs if (newConfigList.remove(oldConfig)) { newConfigList.add(configuration); } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { TableItem[] items= viewer.getTable().getItems(); for (int i = 0; i < items.length; i++) { TableItem item = items[i]; Object data= item.getData(); if (data == oldConfig) { // Found the movedFrom config in the tree. Replace it with the new config item.setData(configuration); viewer.update(configuration, null); break; } } } }); } @Override public void launchConfigurationChanged(ILaunchConfiguration configuration) { } @Override public void launchConfigurationRemoved(ILaunchConfiguration configuration) { } }; /** * Creates an initialized property page */ public BuilderPropertyPage() { super(); noDefaultAndApplyButton(); } /** * Add the project's build to the table viewer. */ private void addBuildersToTable() { IProject project = getInputProject(); if (project == null) { return; } //add build spec entries to the table ICommand[] commands= null; try { commands = project.getDescription().getBuildSpec(); } catch (CoreException e) { handleException(e); return; } boolean projectNeedsMigration= false; for (int i = 0; i < commands.length; i++) { String[] version= new String[] {IExternalToolConstants.EMPTY_STRING}; ILaunchConfiguration config = BuilderUtils.configFromBuildCommandArgs(project, commands[i].getArguments(), version); if (BuilderCoreUtils.VERSION_2_1.equals(version[0])) { // Storing the .project file of a project with 2.1 configs, will // edit the file in a way that isn't backwards compatible. projectNeedsMigration= true; } Object element= null; if (config != null) { if (!config.isWorkingCopy() && !config.exists()) { Shell shell= getShell(); if (shell == null) { return; } IStatus status = new Status(IStatus.ERROR, ExternalToolsPlugin.PLUGIN_ID, 0, NLS.bind(ExternalToolsUIMessages.BuilderPropertyPage_Exists, new String[]{config.getName()}), null); ErrorDialog.openError(getShell(), ExternalToolsUIMessages.BuilderPropertyPage_errorTitle, NLS.bind(ExternalToolsUIMessages.BuilderPropertyPage_External_Tool_Builder__0__Not_Added_2, new String[]{config.getName()}), status); userHasMadeChanges= true; } else { element= config; } } else { String builderID = commands[i].getBuilderName(); if (builderID.equals(ExternalToolBuilder.ID) && commands[i].getArguments().get(BuilderCoreUtils.LAUNCH_CONFIG_HANDLE) != null) { // An invalid external tool entry. element= new ErrorConfig(commands[i]); } else { element= commands[i]; } } if (element != null) { viewer.add(element); viewer.setChecked(element, isEnabled(element)); } } if (projectNeedsMigration) { IPreferenceStore store= ExternalToolsPlugin.getDefault().getPreferenceStore(); boolean prompt= store.getBoolean(IPreferenceConstants.PROMPT_FOR_PROJECT_MIGRATION); boolean proceed= true; if (prompt) { Shell shell= getShell(); if (shell == null) { return; } MessageDialogWithToggle dialog= MessageDialogWithToggle.openYesNoQuestion(shell, ExternalToolsUIMessages.BuilderPropertyPage_0, ExternalToolsUIMessages.BuilderPropertyPage_1, ExternalToolsUIMessages.BuilderPropertyPage_2, false, null, null); proceed= dialog.getReturnCode() == IDialogConstants.YES_ID; store.setValue(IPreferenceConstants.PROMPT_FOR_PROJECT_MIGRATION, !dialog.getToggleState()); } if (!proceed) { // Open the page read-only viewer.getTable().setEnabled(false); downButton.setEnabled(false); editButton.setEnabled(false); importButton.setEnabled(false); newButton.setEnabled(false); removeButton.setEnabled(false); } } } /** * Creates and returns a button with the given label, id, and enablement. */ private Button createButton(Composite parent, String label) { Button button = new Button(parent, SWT.PUSH); button.setFont(parent.getFont()); button.setText(label); button.setEnabled(false); button.addSelectionListener(buttonListener); GridData data = new GridData(GridData.FILL_HORIZONTAL); data.grabExcessHorizontalSpace = true; button.setLayoutData(data); int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x); return button; } /* (non-Javadoc) * Method declared on PreferencePage. */ @Override protected Control createContents(Composite parent) { PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, IExternalToolsHelpContextIds.EXTERNAL_TOOLS_BUILDER_PROPERTY_PAGE); Font font = parent.getFont(); Composite topLevel = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; topLevel.setLayout(layout); topLevel.setLayoutData(new GridData(GridData.FILL_BOTH)); Label description = new Label(topLevel, SWT.WRAP); description.setText(ExternalToolsUIMessages.BuilderPropertyPage_description); description.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); description.setFont(font); Composite tableAndButtons = new Composite(topLevel, SWT.NONE); tableAndButtons.setLayoutData(new GridData(GridData.FILL_BOTH)); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.numColumns = 2; tableAndButtons.setLayout(layout); // table of builders and tools viewer= CheckboxTableViewer.newCheckList(tableAndButtons, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER); viewer.setLabelProvider(labelProvider); viewer.addCheckStateListener(this); Table builderTable= viewer.getTable(); builderTable.setLayoutData(new GridData(GridData.FILL_BOTH)); builderTable.setFont(font); builderTable.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { handleTableSelectionChanged(); } }); builderTable.addListener(SWT.MouseDoubleClick, new Listener() { @Override public void handleEvent(Event event) { //we must not allow editing of elements that cannot be edited via the selection changed logic //https://bugs.eclipse.org/bugs/show_bug.cgi?id=386820 if(fCanEdit) { handleEditButtonPressed(); } } }); //button area Composite buttonArea = new Composite(tableAndButtons, SWT.NONE); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; buttonArea.setLayout(layout); buttonArea.setFont(font); buttonArea.setLayoutData(new GridData(GridData.FILL_VERTICAL)); newButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_newButton); importButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage__Import____3); editButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_editButton); removeButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_removeButton); new Label(buttonArea, SWT.LEFT); upButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_upButton); downButton = createButton(buttonArea, ExternalToolsUIMessages.BuilderPropertyPage_downButton); newButton.setEnabled(true); importButton.setEnabled(true); //populate widget contents addBuildersToTable(); return topLevel; } /** * Turns auto-building on or off in the workspace. */ private void setAutobuild(boolean newState) throws CoreException { IWorkspace workspace= ResourcesPlugin.getWorkspace(); IWorkspaceDescription wsDescription= workspace.getDescription(); boolean oldState= wsDescription.isAutoBuilding(); if (oldState != newState) { wsDescription.setAutoBuilding(newState); workspace.setDescription(wsDescription); } } /** * Returns the project that is the input for this property page, * or <code>null</code>. */ private IProject getInputProject() { IAdaptable element = getElement(); if (element instanceof IProject) { return (IProject) element; } Object resource = element.getAdapter(IResource.class); if (resource instanceof IProject) { return (IProject) resource; } return null; } /** * One of the buttons has been pressed, act accordingly. */ private void handleButtonPressed(Button button) { if (button == newButton) { handleNewButtonPressed(); } else if (button == importButton) { handleImportButtonPressed(); } else if (button == editButton) { handleEditButtonPressed(); } else if (button == removeButton) { handleRemoveButtonPressed(); } else if (button == upButton) { moveSelectionUp(); } else if (button == downButton) { moveSelectionDown(); } if (getControl().isDisposed()) { return; } handleTableSelectionChanged(); viewer.getTable().setFocus(); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent) */ @Override public void checkStateChanged(CheckStateChangedEvent event) { Object element= event.getElement(); boolean checked = event.getChecked(); if (element instanceof ILaunchConfiguration) { enableLaunchConfiguration((ILaunchConfiguration) element, checked); } else if (element instanceof ICommand) { Shell shell= getShell(); if (shell == null) { return; } if (checked) { enableCommand((ICommand)element, checked); return; } else if (!fWarned) { if(MessageDialog.openConfirm(shell, ExternalToolsUIMessages.BuilderPropertyPage_6, ExternalToolsUIMessages.BuilderPropertyPage_7)) { fWarned = true; } } if(fWarned) { enableCommand((ICommand)element, checked); } else { viewer.removeCheckStateListener(this); viewer.setChecked(element, true); viewer.addCheckStateListener(this); } } } private void enableLaunchConfiguration(ILaunchConfiguration configuration, boolean enable) { ILaunchConfigurationWorkingCopy workingCopy= null; try { if (configuration instanceof ILaunchConfigurationWorkingCopy) { workingCopy = (ILaunchConfigurationWorkingCopy) configuration; } else { // Replace the config with a working copy TableItem[] items= viewer.getTable().getItems(); for (int i = 0; i < items.length; i++) { TableItem item = items[i]; if (item.getData() == configuration) { workingCopy = configuration.getWorkingCopy(); item.setData(workingCopy); } } } if (workingCopy != null) { workingCopy.setAttribute(IExternalToolConstants.ATTR_BUILDER_ENABLED, enable); } } catch (CoreException e) { return; } userHasMadeChanges= true; } private void enableCommand(ICommand command, boolean enable) { Map<String, String> args = command.getArguments(); if (args == null) { args = new HashMap<String, String>(1); } args.put(COMMAND_ENABLED, Boolean.toString(enable)); command.setArguments(args); userHasMadeChanges= true; } /** * The user has pressed the import button. Prompt them to select a * configuration to import from the workspace. */ private void handleImportButtonPressed() { ILaunchManager manager= DebugPlugin.getDefault().getLaunchManager(); List<ILaunchConfigurationType> toolTypes = getConfigurationTypes(IExternalToolConstants.ID_EXTERNAL_TOOLS_LAUNCH_CATEGORY); List<ILaunchConfiguration> configurations = new ArrayList<ILaunchConfiguration>(); for (ILaunchConfigurationType type : toolTypes) { try { ILaunchConfiguration[] configs = manager.getLaunchConfigurations(type); for (int i = 0; i < configs.length; i++) { ILaunchConfiguration launchConfiguration = configs[i]; if (!DebugUITools.isPrivate(launchConfiguration)) { configurations.add(launchConfiguration); } } } catch (CoreException e) { } } Shell shell= getShell(); if (shell == null) { return; } ElementListSelectionDialog dialog= new ElementListSelectionDialog(shell, new BuilderLabelProvider()); dialog.setTitle(ExternalToolsUIMessages.BuilderPropertyPage_4); dialog.setMessage(ExternalToolsUIMessages.BuilderPropertyPage_5); dialog.setElements(configurations.toArray()); if (dialog.open() == Window.CANCEL) { return; } Object results[]= dialog.getResult(); if (results.length == 0) { //OK pressed with nothing selected return; } ILaunchConfiguration config= (ILaunchConfiguration) results[0]; ILaunchConfiguration newConfig= null; boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding(); try { setAutobuild(false); newConfig= BuilderUtils.duplicateConfiguration(getInputProject(), config); } catch (CoreException e) { handleException(e); } finally { try { setAutobuild(wasAutobuilding); } catch (CoreException e) { handleException(e); } } if (newConfig != null) { userHasMadeChanges= true; viewer.add(newConfig); viewer.setChecked(newConfig, isEnabled(newConfig)); newConfigList.add(newConfig); } } /** * The user has pressed the remove button. Delete the selected builder. */ private void handleRemoveButtonPressed() { IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); if (selection != null) { int numSelected= selection.size(); userHasMadeChanges= true; Iterator<?> iterator = selection.iterator(); while (iterator.hasNext()) { Object item= iterator.next(); if (item instanceof ILaunchConfiguration) { if (configsToBeDeleted == null) { configsToBeDeleted = new ArrayList<ILaunchConfiguration>(numSelected); } configsToBeDeleted.add((ILaunchConfiguration) item); } else if (item instanceof ICommand) { if (commandsToBeDeleted == null) { commandsToBeDeleted = new ArrayList<ICommand>(numSelected); } commandsToBeDeleted.add((ICommand) item); } viewer.remove(item); } } } /** * The user has pressed the new button. Create a new configuration and open * the launch configuration edit dialog on the new config. */ private void handleNewButtonPressed() { ILaunchConfigurationType type = promptForConfigurationType(); if (type == null) { return; } boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding(); try { ILaunchConfigurationWorkingCopy workingCopy = null; String name= DebugPlugin.getDefault().getLaunchManager().generateLaunchConfigurationName(ExternalToolsUIMessages.BuilderPropertyPage_New_Builder_7); workingCopy = type.newInstance(BuilderUtils.getBuilderFolder(getInputProject(), true), name); StringBuffer buffer= new StringBuffer(IExternalToolConstants.BUILD_TYPE_FULL); buffer.append(','); buffer.append(IExternalToolConstants.BUILD_TYPE_INCREMENTAL); buffer.append(','); workingCopy.setAttribute(IExternalToolConstants.ATTR_RUN_BUILD_KINDS, buffer.toString()); workingCopy.setAttribute(ExternalToolsMainTab.FIRST_EDIT, true); ILaunchConfiguration config = null; setAutobuild(false); config = workingCopy.doSave(); //needs to be added here in case the user hits apply in the edit dialog //then we can correctly update the list with the new config. newConfigList.add(config); int code= editConfiguration(config); if (code == Window.CANCEL) { // If the user cancelled, delete the newly created config newConfigList.remove(config); config.delete(); } else { userHasMadeChanges= true; //retrieve the last "new" config //may have been changed by the user pressing apply in the edit dialog config= newConfigList.get(newConfigList.size() - 1); viewer.add(config); viewer.setChecked(config, isEnabled(config)); } } catch (CoreException e) { handleException(e); } finally { try { setAutobuild(wasAutobuilding); } catch (CoreException e) { handleException(e); } } } /** * Prompts the user to edit the given launch configuration. Returns the * return code from opening the launch configuration dialog. */ private int editConfiguration(ILaunchConfiguration config) { ILaunchManager manager= DebugPlugin.getDefault().getLaunchManager(); manager.addLaunchConfigurationListener(configurationListener); Shell shell= getShell(); if (shell == null) { return Window.CANCEL; } int code= DebugUITools.openLaunchConfigurationPropertiesDialog(shell, config, org.eclipse.ui.externaltools.internal.model.IExternalToolConstants.ID_EXTERNAL_TOOLS_BUILDER_LAUNCH_GROUP); manager.removeLaunchConfigurationListener(configurationListener); return code; } /** * Prompts the user to choose a launch configuration type to create and * returns the type the user selected or <code>null</code> if the user * cancelled. * * @return the configuration type selected by the user or <code>null</code> * if the user cancelled. */ private ILaunchConfigurationType promptForConfigurationType() { List<ILaunchConfigurationType> externalToolTypes = getConfigurationTypes(IExternalToolConstants.ID_EXTERNAL_TOOLS_BUILDER_LAUNCH_CATEGORY); Shell shell= getShell(); if (shell == null) { return null; } ElementListSelectionDialog dialog = new ElementListSelectionDialog(shell, new BuilderLabelProvider()); dialog.setElements(externalToolTypes.toArray()); dialog.setMultipleSelection(false); dialog.setTitle(ExternalToolsUIMessages.BuilderPropertyPage_Choose_configuration_type_8); dialog.setMessage(ExternalToolsUIMessages.BuilderPropertyPage_Choose_an_external_tool_type_to_create_9); dialog.open(); Object result[] = dialog.getResult(); if (result == null || result.length == 0) { return null; } return (ILaunchConfigurationType) result[0]; } /** * Returns the launch configuration types of the given category */ private List<ILaunchConfigurationType> getConfigurationTypes(String category) { ILaunchConfigurationType types[] = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationTypes(); List<ILaunchConfigurationType> externalToolTypes = new ArrayList<ILaunchConfigurationType>(); for (int i = 0; i < types.length; i++) { ILaunchConfigurationType configurationType = types[i]; if (category.equals(configurationType.getCategory())) { externalToolTypes.add(configurationType); } } return externalToolTypes; } /** * The user has pressed the edit button or double-clicked. Open the launch configuration edit * dialog on the selection after migrating the tool if necessary. */ private void handleEditButtonPressed() { TableItem[] items= viewer.getTable().getSelection(); if (items.length == 0) { return; } TableItem selection= items[0]; if (selection != null) { Object data = selection.getData(); if (data instanceof ILaunchConfiguration) { ILaunchConfiguration config= (ILaunchConfiguration) data; if (BuilderUtils.isUnmigratedConfig(config)) { if (!shouldProceedWithMigration()) { return; } try { config= BuilderUtils.migrateBuilderConfiguration(getInputProject(), (ILaunchConfigurationWorkingCopy) config); } catch (CoreException e) { handleException(e); return; } // Replace the working copy in the table with the migrated configuration selection.setData(config); } userHasMadeChanges= true; boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding(); try { setAutobuild(false); editConfiguration(config); } catch (CoreException e) { handleException(e); } finally { try { setAutobuild(wasAutobuilding); } catch (CoreException e) { handleException(e); } } } else if (data instanceof ICommand) { ICommand command= (ICommand) data; if (command.isConfigurable()) { if (editCommand(command)) { userHasMadeChanges= true; } } } } } private boolean editCommand(ICommand data) { EditCommandDialog dialog= new EditCommandDialog(getShell(), data); return Window.OK == dialog.open(); } /** * Prompts the user to proceed with the migration of a project builder from * the old format to the new, launch configuration-based, format and returns * whether or not the user wishes to proceed with the migration. * * @return boolean whether or not the user wishes to proceed with migration */ private boolean shouldProceedWithMigration() { if (!ExternalToolsPlugin.getDefault().getPreferenceStore().getBoolean(IPreferenceConstants.PROMPT_FOR_TOOL_MIGRATION)) { // User has asked not to be prompted return true; } Shell shell= getShell(); if (shell == null) { return false; } // Warn the user that editing an old config will cause storage migration. MessageDialogWithToggle dialog= MessageDialogWithToggle.openYesNoQuestion(getShell(), ExternalToolsUIMessages.BuilderPropertyPage_Migrate_project_builder_10, ExternalToolsUIMessages.BuilderPropertyPage_Not_Support, ExternalToolsUIMessages.BuilderPropertyPage_Prompt, false, ExternalToolsPlugin.getDefault().getPreferenceStore(), IPreferenceConstants.PROMPT_FOR_TOOL_MIGRATION); return dialog.getReturnCode() == IDialogConstants.YES_ID; } /** * Handles unexpected internal exceptions */ private void handleException(Exception e) { final IStatus[] status= new IStatus[1]; if (e instanceof CoreException) { status[0] = ((CoreException) e).getStatus(); } else { status[0] = new Status(IStatus.ERROR, ExternalToolsPlugin.PLUGIN_ID, 0, ExternalToolsUIMessages.BuilderPropertyPage_statusMessage, e); } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { Shell shell= getShell(); if (shell != null) { ErrorDialog.openError(shell, ExternalToolsUIMessages.BuilderPropertyPage_errorTitle, ExternalToolsUIMessages.BuilderPropertyPage_errorMessage, status[0]); } } }); } /** * The user has selected a different builder in table. * Update button enablement. */ private void handleTableSelectionChanged() { newButton.setEnabled(true); Table builderTable= viewer.getTable(); TableItem[] items = builderTable.getSelection(); fCanEdit = false; boolean enableRemove = false; boolean enableUp = false; boolean enableDown = false; if(items != null) { boolean validSelection = items.length > 0; fCanEdit = validSelection; enableRemove = validSelection; enableUp = validSelection; enableDown = validSelection; if (items.length > 1) { fCanEdit= false; } int indices[]= builderTable.getSelectionIndices(); int max = builderTable.getItemCount(); if(indices.length > 0) { enableUp = indices[0] != 0; enableDown = indices[indices.length - 1] < max - 1; } for (int i = 0; i < items.length; i++) { TableItem item = items[i]; Object data= item.getData(); if (data instanceof ILaunchConfiguration) { ILaunchConfiguration config= (ILaunchConfiguration)data; String builderName= null; try { builderName = config.getAttribute(IExternalToolConstants.ATTR_DISABLED_BUILDER, (String)null); } catch (CoreException e) { } if (builderName != null) { //do not allow "wrapped" builders to be removed or edited if they are valid IExtension ext= Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, builderName); fCanEdit= false; enableRemove= ext == null; } } else { if (data instanceof ErrorConfig) { fCanEdit= false; continue; } ICommand command= (ICommand) data; fCanEdit= command.isConfigurable(); IExtension ext= Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, command.getBuilderName()); enableRemove= ext == null; break; } } } editButton.setEnabled(fCanEdit); removeButton.setEnabled(enableRemove); upButton.setEnabled(enableUp); downButton.setEnabled(enableDown); } /** * Returns whether the given element (command or launch config) * is enabled. * * @param element the element * @return whether the given element is enabled */ private boolean isEnabled(Object element) { if (element instanceof ICommand) { ICommand command = (ICommand) element; String val = command.getArguments().get(COMMAND_ENABLED); if(val != null) { //null means enabled, see #doPerformOk return Boolean.parseBoolean(val); } } else if (element instanceof ILaunchConfiguration) { try { return ExternalToolsUtil.isBuilderEnabled((ILaunchConfiguration) element); } catch (CoreException e) { } } else if (element instanceof ErrorConfig) { return false; } return true; } /** * Moves an entry in the builder table to the given index. */ private void move(TableItem item, int index) { userHasMadeChanges= true; Object data = item.getData(); item.dispose(); viewer.insert(data, index); viewer.setChecked(data, isEnabled(data)); } /** * Move the current selection in the build list down. */ private void moveSelectionDown() { Table builderTable= viewer.getTable(); int indices[]= builderTable.getSelectionIndices(); if (indices.length < 1) { return; } int newSelection[]= new int[indices.length]; int max= builderTable.getItemCount() - 1; for (int i = indices.length - 1; i >= 0; i--) { int index= indices[i]; if (index < max) { move (builderTable.getItem(index), index + 1); newSelection[i]= index + 1; } } builderTable.setSelection(newSelection); } /** * Move the current selection in the build list up. */ private void moveSelectionUp() { Table builderTable= viewer.getTable(); int indices[]= builderTable.getSelectionIndices(); int newSelection[]= new int[indices.length]; for (int i = 0; i < indices.length; i++) { int index= indices[i]; if (index > 0) { move (builderTable.getItem(index), index - 1); newSelection[i]= index - 1; } } builderTable.setSelection(newSelection); } /* (non-Javadoc) * @see org.eclipse.jface.preference.IPreferencePage#performOk() */ @Override public boolean performOk() { if (!userHasMadeChanges) { return super.performOk(); } userHasMadeChanges= false; Table builderTable= viewer.getTable(); int numCommands = builderTable.getItemCount(); final Object[] itemData= new Object[numCommands]; for (int i = 0; i < numCommands; i++) { itemData[i]= builderTable.getItem(i).getData(); } IRunnableWithProgress runnable= new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { doPerformOk(monitor, itemData); if (monitor.isCanceled()) { throw new InterruptedException(); } } }; IProgressService service= PlatformUI.getWorkbench().getProgressService(); try { service.busyCursorWhile(runnable); } catch (InvocationTargetException e) { return false; } catch (InterruptedException e) { return false; } return super.performOk(); } private void doPerformOk(IProgressMonitor monitor, Object[] itemData) { if (monitor.isCanceled()) { return; } IProject project = getInputProject(); //get all the build commands int numCommands = itemData.length; monitor.beginTask(ExternalToolsUIMessages.BuilderPropertyPage_3, numCommands + 1); List<ICommand> possibleCommands = new ArrayList<ICommand>(numCommands); for (int i = 0; i < numCommands; i++) { Object data = itemData[i]; if (data instanceof ICommand) { if (commandsToBeDeleted != null && commandsToBeDeleted.contains(data)) { //command specified to be removed data= null; continue; } ICommand command= (ICommand)data; Map<String, String> args = command.getArguments(); String val = args.get(COMMAND_ENABLED); if(val != null) { if (!Boolean.parseBoolean(val)) { ILaunchConfiguration config= disableCommand(command); if (config != null) { data= BuilderUtils.commandFromLaunchConfig(project,config); } } else { args.remove(COMMAND_ENABLED); command.setArguments(args); } } } else if (data instanceof ILaunchConfiguration) { ILaunchConfiguration config= (ILaunchConfiguration) data; String disabledBuilderName; try { disabledBuilderName = config.getAttribute(IExternalToolConstants.ATTR_DISABLED_BUILDER, (String)null); if (disabledBuilderName != null && ExternalToolsUtil.isBuilderEnabled(config)) { possibleCommands.add(translateBackToCommand(config, project)); continue; } } catch (CoreException e1) { } if (!BuilderUtils.isUnmigratedConfig(config) && (config instanceof ILaunchConfigurationWorkingCopy)) { ILaunchConfigurationWorkingCopy workingCopy= ((ILaunchConfigurationWorkingCopy) config); // Save any changes to the config (such as enable/disable) if (workingCopy.isDirty()) { try { workingCopy.doSave(); } catch (CoreException e) { Shell shell= getShell(); if (shell != null) { MessageDialog.openError(shell, ExternalToolsUIMessages.BuilderPropertyPage_39, NLS.bind(ExternalToolsUIMessages.BuilderPropertyPage_40, new String[] {workingCopy.getName()})); } } } } data= BuilderUtils.commandFromLaunchConfig(project, config); } else if (data instanceof ErrorConfig) { data= ((ErrorConfig) data).getCommand(); } if (data instanceof ICommand) { possibleCommands.add((ICommand) data); } monitor.worked(1); } ICommand[] commands= new ICommand[possibleCommands.size()]; possibleCommands.toArray(commands); if (checkCommandsForChange(commands)) { //set the build spec try { IProjectDescription desc = project.getDescription(); desc.setBuildSpec(commands); project.setDescription(desc, IResource.FORCE, monitor); } catch (CoreException e) { handleException(e); performCancel(); } } if (configsToBeDeleted != null) { deleteConfigurations(); } monitor.done(); } private void checkBuilderFolder() { try { IFolder builderFolder= BuilderUtils.getBuilderFolder(getInputProject(), false); if (builderFolder != null && builderFolder.exists() && builderFolder.members().length == 0) { // All files in the builder folder have been deleted. Clean up builderFolder.delete(true, false, null); } } catch (CoreException e) { handleException(e); } } /** * A non-external tool builder builder was disabled. * It has been re-enabled. Translate the disabled external tool builder launch configuration * wrapper back into the full fledged builder command. */ private ICommand translateBackToCommand(ILaunchConfiguration config, IProject project) { try { ICommand newCommand = project.getDescription().newCommand(); String builderName= config.getAttribute(IExternalToolConstants.ATTR_DISABLED_BUILDER, (String)null); Map<String, String> args = config.getAttribute(IExternalToolConstants.ATTR_TOOL_ARGUMENTS, new HashMap<String, String>(0)); newCommand.setBuilderName(builderName); newCommand.setArguments(args); if (configsToBeDeleted == null) { configsToBeDeleted = new ArrayList<ILaunchConfiguration>(); } configsToBeDeleted.add(config); return newCommand; } catch (CoreException exception) { Shell shell= getShell(); if (shell != null) { MessageDialog.openError(shell, ExternalToolsUIMessages.BuilderPropertyPage_13, ExternalToolsUIMessages.BuilderPropertyPage_error); } return null; } } /** * Disables a builder by wrapping the builder command as a disabled external * tool builder. The details of the command is persisted in the launch * configuration. */ private ILaunchConfiguration disableCommand(ICommand command) { Map<String, String> arguments = command.getArguments(); if (arguments != null) { arguments.remove(COMMAND_ENABLED); } List<ILaunchConfigurationType> externalToolTypes = getConfigurationTypes(IExternalToolConstants.ID_EXTERNAL_TOOLS_BUILDER_LAUNCH_CATEGORY); if (externalToolTypes.size() == 0) { return null; } ILaunchConfigurationType type= externalToolTypes.get(0); if (type == null) { return null; } boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding(); try { ILaunchConfigurationWorkingCopy workingCopy = null; String builderName = command.getBuilderName(); String name= DebugPlugin.getDefault().getLaunchManager().generateLaunchConfigurationName(builderName); workingCopy = type.newInstance(BuilderUtils.getBuilderFolder(getInputProject(), true), name); workingCopy.setAttribute(IExternalToolConstants.ATTR_DISABLED_BUILDER, builderName); if (arguments != null) { workingCopy.setAttribute(IExternalToolConstants.ATTR_TOOL_ARGUMENTS, arguments); } workingCopy.setAttribute(IExternalToolConstants.ATTR_BUILDER_ENABLED, false); ILaunchConfiguration config = null; setAutobuild(false); config = workingCopy.doSave(); return config; } catch (CoreException e) { handleException(e); } finally { try { setAutobuild(wasAutobuilding); } catch (CoreException e) { handleException(e); } } return null; } private void deleteConfigurations() { boolean wasAutobuilding= ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding(); try { setAutobuild(false); for (ILaunchConfiguration config : configsToBeDeleted) { config.delete(); } checkBuilderFolder(); } catch (CoreException e) { handleException(e); } finally { try { setAutobuild(wasAutobuilding); } catch (CoreException e) { handleException(e); } } } /** * Returns whether any of the commands have changed. */ private boolean checkCommandsForChange(ICommand[] newCommands) { try { ICommand[] oldCommands = getInputProject().getDescription().getBuildSpec(); if (oldCommands.length != newCommands.length) { return true; } IgnoreWhiteSpaceComparator comparator= new IgnoreWhiteSpaceComparator(); for (int i = 0; i < oldCommands.length; i++) { ICommand oldCommand = oldCommands[i]; ICommand newCommand= newCommands[i]; String oldName= oldCommand.getBuilderName(); String newName= newCommand.getBuilderName(); if (oldName == null && newName != null) { return true; } if(oldName != null && !oldName.equals(newName)) { return true; } Map<String, String> oldArgs = oldCommand.getArguments(); Map<String, String> newArgs = newCommand.getArguments(); if (oldArgs == null) { if(newArgs != null) { return true; } continue; } if(oldArgs.size() != newArgs.size()) { return true; } for (String key : oldArgs.keySet()) { if (comparator.compare(oldArgs.get(key), newArgs.get(key)) != 0) { return true; } } if (oldCommand.isBuilding(IncrementalProjectBuilder.AUTO_BUILD) != newCommand.isBuilding(IncrementalProjectBuilder.AUTO_BUILD) || oldCommand.isBuilding(IncrementalProjectBuilder.CLEAN_BUILD) != newCommand.isBuilding(IncrementalProjectBuilder.CLEAN_BUILD) || oldCommand.isBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD) != newCommand.isBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD) || oldCommand.isBuilding(IncrementalProjectBuilder.FULL_BUILD) != newCommand.isBuilding(IncrementalProjectBuilder.FULL_BUILD)) { return true; } } } catch (CoreException ce) { return true; } return false; } /* (non-Javadoc) * @see org.eclipse.jface.preference.IPreferencePage#performCancel() */ @Override public boolean performCancel() { for (ILaunchConfiguration config : newConfigList) { try { config.delete(); } catch (CoreException e) { handleException(e); } } checkBuilderFolder(); //remove the local marking of the enabled state of the commands Table builderTable= viewer.getTable(); int numCommands = builderTable.getItemCount(); for (int i = 0; i < numCommands; i++) { Object data = builderTable.getItem(i).getData(); if (data instanceof ICommand) { ICommand command= (ICommand)data; Map<String, String> args = command.getArguments(); args.remove(COMMAND_ENABLED); command.setArguments(args); } } return super.performCancel(); } @Override public Shell getShell() { if (getControl().isDisposed()) { return null; } return super.getShell(); } }